package com.sdy.healthcode.h5.controller.user;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sdy.common.model.BizException;
import com.sdy.common.model.Response;
import com.sdy.common.utils.MapUtil;
import com.sdy.healthcode.biz.constants.ParamConstants;
import com.sdy.healthcode.biz.constants.RedisConstants;
import com.sdy.healthcode.biz.dto.DepartmentDTO;
import com.sdy.healthcode.biz.dto.DoctorDTO;
import com.sdy.healthcode.biz.dto.DoctorDetailDTO;
import com.sdy.healthcode.biz.dto.ExpertApplyDTO;
import com.sdy.healthcode.biz.dto.ExpertsAppointmentDTO;
import com.sdy.healthcode.biz.dto.ScheduleDTO;
import com.sdy.healthcode.biz.service.ExpertsAppointmentService;
import com.sdy.healthcode.biz.service.HsDepartmentService;
import com.sdy.healthcode.biz.service.SysParamService;
import com.sdy.mvc.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;


@Slf4j
@RestController
@RequestMapping("/doctor/appointment")
@Api(tags = "医生预约")
public class DoctorAppointmentController extends BaseController {
    @Autowired
    private ExpertsAppointmentService expertsAppointmentService;
    @Autowired
    private SysParamService sysParamService;
    @Autowired
    private RedisLockRegistry redisLockRegistry;
    @Autowired
    private HsDepartmentService hsDepartmentService;

    @GetMapping("/rule")
    @ApiOperation(value = "预约规则")
    public Response<String> rule() {
        String rule = sysParamService.getOrDefault(ParamConstants.EXPERTS_APPOINTMENT_RULE, "");
        String cnt = sysParamService.getOrDefault(ParamConstants.EXPERTS_APPOINTMENT_BLACK_LIST_COUNT, "3");
        String days = sysParamService.getOrDefault(ParamConstants.EXPERTS_APPOINTMENT_BLACK_LIST_DAYS, "30");
        rule = String.format(rule, cnt, days);
        return Response.success(rule);
    }
    
    @GetMapping("/departmentInfo")
    @ApiOperation(value = "科室数据")
    public Response<List<DepartmentDTO>> departmentInfo() {
        List<DepartmentDTO> rootList = new ArrayList<>();
        List<DepartmentDTO> listData = hsDepartmentService.list()
                .stream()
                .map(d -> new DepartmentDTO()
                        .setName(d.getName())
                        .setParent(null)
                        .setSort(d.getSort() == null ? 0 : d.getSort())
                        .setRules(d.getRules())
                        .setValue(d.getId()))
                .collect(Collectors.toList());
        Map<Integer, DepartmentDTO> m = MapUtil.collectionToMap(listData, DepartmentDTO::getValue);
        for (DepartmentDTO department : listData) {
            DepartmentDTO parent = m.get(department.getParent());
            if (parent != null) {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(department);
            } else {
                rootList.add(department);
            }
        }
        rootList.sort((o1, o2) -> {
            if (o1.getSort().equals(o2.getSort())) {
                return o1.getValue() - o2.getValue(); 
            } else {
                return o1.getSort() - o2.getSort();
            }
        });
        return Response.success(rootList);
    }

    @GetMapping("/registerInfoOfDepartment")
    @ApiOperation(value = "科室的有无号数据")
    @ApiImplicitParam(value = "科室value", name = "dept")
    public Response<List<ScheduleDTO>> registerInfoOfDepartment(Integer dept) throws BizException {
        return Response.success(expertsAppointmentService.registerInfoOfDepartment(dept));
    }

    @GetMapping("/registerInfoOfDepartmentAndDay")
    @ApiOperation(value = "某科室某天挂号数据")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(value = "科室value", name = "dept"),
            @ApiImplicitParam(value = "日期", name = "date")
    })
    public Response<List<DoctorDTO>> registerInfoOfDepartmentAndDay(Integer dept, String date) {
        return Response.success(expertsAppointmentService.registerInfoOfDepartmentAndDay(dept, date));
    }

    @GetMapping("/expertScheduleDetail")
    @ApiOperation(value = "专家预约信息详情")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(value = "科室value", name = "dept"),
            @ApiImplicitParam(value = "日期", name = "date")
    })
    public Response<DoctorDetailDTO> expertScheduleDetail(Integer doctorId, String date) {
        return Response.success(expertsAppointmentService.expertScheduleDetail(doctorId, date));
    }
    
    @PostMapping("/apply")
    @ApiOperation(value = "挂号预约")
    public Response apply(HttpServletRequest request, @RequestBody ExpertApplyDTO expertApply) throws BizException, InterruptedException {
        Lock lockUser = redisLockRegistry.obtain(RedisConstants.LOCK_APPLY_EXPERT_USER + getUserId(request));
        Lock lockDoctor = redisLockRegistry.obtain(RedisConstants.LOCK_APPLY_EXPERT_DOCTOR + expertApply.getExpertsScheduleDailyId());
        if (!lockUser.tryLock(10, TimeUnit.SECONDS)) {
            return Response.error("请求过于频繁，请稍后再试。");
        }
        try {
            if (!lockDoctor.tryLock(10, TimeUnit.SECONDS)) {
                return Response.error("服务器繁忙，请稍后再试。");
            }
            try {
                Integer r = expertsAppointmentService.tryExpertAppointment(
                        getUserId(request),
                        expertApply.getExpertsScheduleDailyId(),
                        expertApply.getUserPatientId());
                if (r == null) {
                    return Response.error("该时段已约满，请重新预约。");
                }
            } finally {
                lockDoctor.unlock();
            }
        } finally {
            lockUser.unlock();
        }
        return Response.success();
    }

    @PostMapping("/cancelApply")
    @ApiOperation(value = "取消挂号预约")
    @ApiImplicitParam(value = "预约ID", name = "applyId")
    public Response cancelApply(HttpServletRequest request, Integer applyId) throws BizException {
        expertsAppointmentService.cancelApply(getUserId(request), applyId);
        return Response.success();
    }

    @GetMapping("/pageApplyInfo")
    @ApiOperation("用户科室预约分页信息")
    public Response<IPage<ExpertsAppointmentDTO>> pageApplyInfo(HttpServletRequest request, Page<ExpertsAppointmentDTO> page) {
        return Response.success(expertsAppointmentService.pageExpertsAppointmentInfo(page, getUserId(request), null, null));
    }

    @GetMapping("/countCancelApplyTimes")
    @ApiOperation("获取用户取消预约次数")
    public Response countCancelApplyTimes(HttpServletRequest request) {
        int cancelCount = expertsAppointmentService.countCancelApply(getUserId(request));
        int cancelLimit = Integer.valueOf(sysParamService.getOrDefault(ParamConstants.EXPERTS_APPOINTMENT_BLACK_LIST_COUNT, "3"));
        int banDays = Integer.valueOf(sysParamService.getOrDefault(ParamConstants.EXPERTS_APPOINTMENT_BLACK_LIST_DAYS, "30"));
        Map<String, Object> result = new HashMap<>();
        result.put("cancelCount", cancelCount);
        result.put("cancelLimit", cancelLimit);
        result.put("banDays", banDays);
        return Response.success(result);
    }
}
